package com.xb.cloud.gateway.core.engine;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.alibaba.fastjson.JSON;
import com.xb.cloud.common.model.ExceptionLoggerDetail;
import com.xb.cloud.gateway.client.Adapter;
import com.xb.cloud.gateway.client.Context;
import com.xb.cloud.gateway.client.ExceptionWrapper;
import com.xb.cloud.gateway.client.RtnInfo;
import com.xb.cloud.gateway.common.constant.GatewayExceptionType;
import com.xb.cloud.gateway.core.RtnInfoCreater;

/**
 * 
 * @ClassName: XBDispatcher
 * @Description:请求分发器
 * @author: 鲜备科技
 * @date: 2017年12月21日 下午4:37:51
 * 
 * @Copyright: 2017 www.xianbeitech.com Inc. All rights reserved.
 *
 */
@Component()
public class XBDispatcher implements ApplicationContextAware {
	private static Logger logger = LoggerFactory.getLogger(XBDispatcher.class);
	/**
	 * spring 上下文
	 */
	private ApplicationContext beanContext;

	/**
	 * 适配器集合 用于适配器批量装配 （key: url是adapter.xml中urlMappingPath的值
	 * 有可能是：/xxxx/xxxx/v2.0）
	 */
	private static Map<String, Adapter> adapterMap = new HashMap<String, Adapter>();

	/**
	 * 目标适配器集合 用于目标适配器匹配（key:url是对 adapter.xml中的规范处理后的url 一定都是：/xxxx/xxxx）
	 */
	private static ConcurrentHashMap<String, Adapter> targetAdapterMap = new ConcurrentHashMap<String, Adapter>();

	/**
	 * 初始化完成适配器读取和装配 （ 会被 主servlet加载时调用）
	 */
	public void init() {
		// TODO:可以调用配置中心或者ZK获取网关初始化数据
		initUrlAdapterMap();
		// TODO:可以干预adapter调用服务的超时时间
	}

	private void initUrlAdapterMap() {
		Map<String, Adapter> beanMap = beanContext.getBeansOfType(Adapter.class);
		if (beanMap != null) {
			for (String beanName : beanMap.keySet()) {
				Adapter adapter = beanMap.get(beanName);
				if (adapter.getUrlMappingPath() == null) {
					ExceptionLoggerDetail loggerDetail = new ExceptionLoggerDetail(this.getClass().getName(),
							"initUrlAdapterMap");
					loggerDetail.setExceptionType(GatewayExceptionType.ADAPTER_NO_PATH.getExceptionType());
					loggerDetail
							.setExceptionDetail(GatewayExceptionType.ADAPTER_NO_PATH.getExceptionDetail() + beanName);
					logger.error(JSON.toJSONString(loggerDetail));
					continue;
				}

				if (!adapterMap.keySet().contains(adapter.getUrlMappingPath())) {
					adapterMap.put(adapter.getUrlMappingPath(), adapter);

				} else {
					ExceptionLoggerDetail loggerDetail = new ExceptionLoggerDetail(this.getClass().getName(),
							"initUrlAdapterMap");
					loggerDetail.setExceptionType(GatewayExceptionType.ADAPTER_DUPLICATION_PATH.getExceptionType());
					loggerDetail.setExceptionDetail(
							GatewayExceptionType.ADAPTER_DUPLICATION_PATH.getExceptionDetail() + beanName);
					logger.error(JSON.toJSONString(loggerDetail));
				}
			}
		}
		
		logger.info("<----------------------系统URL与Adapter的映射关系-------------->"); 
		List<String> urlList=new ArrayList<String>();
		urlList.addAll(adapterMap.keySet());
		Collections.sort(urlList);
		for(String url:urlList){
			logger.info("["+url+"]" +" => "+adapterMap.get(url).getClass().getName());
		}
	}

	/**
	 * 
	 * @Title: execute @Description: 调用Adapter @param: @param
	 *         context @param: @param adapter @return: void @throws
	 */
	public void execute(Context context, Adapter adapter) {

		RtnInfo rtnInfo = null;
		try {

			rtnInfo = adapter.execute(context.getUrlPath(), context.getCurrentUserId() != null,
					context.getRequestInfo().getBizPara(), context);
		} catch (Exception e) {// 对adapter未catch异常进行捕获，避免异常上报至客户端
//			Object[] args = {};
//			args[0] = context;
			ExceptionLoggerDetail loggerDetail = new ExceptionLoggerDetail(e, adapter.getClass().getName(), "execute",
					new Object[]{context});
			logger.error(JSON.toJSONString(loggerDetail));
			context.setRtnInfo(RtnInfoCreater.InvokeAdapterError(e.getMessage()));
			context.addWLException(ExceptionWrapper.ERROR_TYPE_ADAPTER, adapter.getClass().getName(), e);
		}

		if (rtnInfo != null) {
			context.setRtnInfo(rtnInfo);
		} else {
			// TODO:给出合理的错误，用于界面展示
		}

	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.beanContext = applicationContext;

	}

	public Adapter getAdapter(String url) {
		if (targetAdapterMap.containsKey(url)) {
			return targetAdapterMap.get(url);
		}

		// 优先采用spring AntPathMatcher匹配方式
		Adapter adapter = AntPathMapStrategy.GetInstance().execute(adapterMap, url);

		// 未匹配到的采用原始匹配逻辑
		if (adapter == null) {
			adapter = CombineStrategy.GetInstance().execute(adapterMap, url);
		}

		// Adapter adapter = WlMatcher.getInstance(adapterMap).execute(url);
		if (adapter != null) {
			targetAdapterMap.put(url, adapter);
		}

		return adapter;
	}

	private interface AdapterMapStrategy {
		public Adapter execute(Map<String, Adapter> map, String urlPath);
	}

	/**
	 * 
	 * 整合了所有的模式，一个一个去匹配url
	 * 
	 */
	private static class CombineStrategy implements AdapterMapStrategy {
		private static AdapterMapStrategy strategy = null;

		public synchronized static AdapterMapStrategy GetInstance() {
			if (strategy == null) {
				AdapterMapStrategy[] strategies = new AdapterMapStrategy[] { new PreciseMapStrategy(),
						new RegexMapStrategy(), new RegexMapStrategyEnhance(), new ModuleMapStrategy() };
				strategy = new CombineStrategy(strategies);
			}
			return strategy;
		}

		private AdapterMapStrategy[] strategies;

		private CombineStrategy(AdapterMapStrategy... strategies) {
			this.strategies = strategies;
		}

		public Adapter execute(Map<String, Adapter> map, String urlPath) {

			if (strategies == null)
				return null;

			urlPath = urlPath.toLowerCase();

			for (AdapterMapStrategy strategy : strategies) {
				Adapter adapter = strategy.execute(map, urlPath);
				if (adapter != null)
					return adapter;
			}
			return null;

		}

	}

	/**
	 * 
	 * 正则表达式匹配 /shopping/a* 没有被精确匹配时可以匹配 /shopping/abc
	 * 
	 * @Author wangxiaowu
	 * @CreateTime 2014-8-1 下午03:32:20
	 */
	private static class RegexMapStrategy implements AdapterMapStrategy {
		public Adapter execute(Map<String, Adapter> map, String urlPath) {
			if (map == null)
				return null;

			String frontPast = urlPath.substring(0, urlPath.lastIndexOf("/"));
			// 循环匹配
			for (Entry<String, Adapter> entry : map.entrySet()) {
				String url = entry.getKey();
				// 前半部分相同则用正则表达式去匹配
				int index = url.lastIndexOf("/");
				if (index < 0) {
					continue;
				}
				if (frontPast.equalsIgnoreCase(url.substring(0, index))) {
					String laststr = url.substring(index + 1);
					// 如果后面只有一个*则直接匹配
					if ("*".equals(laststr)) {
						return entry.getValue();
					}
					Pattern pattern = Pattern.compile(url);
					Matcher matcher = pattern.matcher(urlPath);
					if (matcher.find()) {
						return entry.getValue();
					}
				}
			}

			return null;
		}

	}

	/**
	 * 
	 * 正则表达式匹配 查缺补漏 /shopping/* *支持多层
	 */
	private static class RegexMapStrategyEnhance implements AdapterMapStrategy {
		public Adapter execute(Map<String, Adapter> map, String urlPath) {
			if (map == null)
				return null;

			for (Entry<String, Adapter> entry : map.entrySet()) {
				String url = entry.getKey();
				int index = url.lastIndexOf("/");
				String laststr = url.substring(index + 1);
				if ("*".equals(laststr)) {
					Pattern pattern = Pattern.compile("^" + url);
					Matcher matcher = pattern.matcher(urlPath);
					if (matcher.find()) {
						return entry.getValue();
					}
				}
			}
			return null;
		}

	}

	/**
	 * 
	 * 模块匹配 /shopping/aaa 没有被精确匹配时可以匹配 /shopping/
	 * 
	 * @Author wangxiaowu
	 * @CreateTime 2014-8-1 下午03:32:20
	 */
	private static class ModuleMapStrategy implements AdapterMapStrategy {
		public Adapter execute(Map<String, Adapter> map, String urlPath) {
			if (map == null)
				return null;

			while (urlPath.lastIndexOf("/") > 0) {
				urlPath = urlPath.substring(0, urlPath.lastIndexOf("/"));
				Adapter adapter = map.get(urlPath);
				if (adapter != null) {
					return adapter;
				}
			}
			return null;
		}
	}

	/**
	 * 
	 * 精确匹配
	 * 
	 * @Author wangxiaowu
	 * @CreateTime 2014-8-1 下午03:35:33
	 */
	private static class PreciseMapStrategy implements AdapterMapStrategy {
		public Adapter execute(Map<String, Adapter> map, String urlPath) {

			if (map == null)
				return null;
			return map.get(urlPath);
		}
	}

	/**
	 * spring AntPathMatcher 匹配策略 add by zgz 2016-07-19
	 */
	private static class AntPathMapStrategy {

		private static AntPathMapStrategy strategy = null;

		public synchronized static AntPathMapStrategy GetInstance() {
			if (strategy == null) {
				strategy = new AntPathMapStrategy();
			}
			return strategy;
		}

		public Adapter execute(Map<String, Adapter> map, String urlPath) {
			PathMatcher matcher = new AntPathMatcher();
			for (Entry<String, Adapter> entry : map.entrySet()) {
				String url = entry.getKey();
				if (url == null || url.isEmpty()) {
					continue;
				}
				if (matcher.match(url, urlPath)) {
					return entry.getValue();
				}
			}
			return null;
		}

	}

}
